﻿using System.Text;

namespace Beeflys.Generator
{
    class ProtocolReqAdapterGenerator : IGenerator
    {
        private const string A_MESSENGER_REQ_ADAPTER = "Beeflys.AMessengerReqAdapter";
        

        public string Name => throw new NotImplementedException();

        public string ReqAdapterName { get; private set; }


        private StringBuilder sb = new StringBuilder();

        public void Start(UTypeInfo typeInfo)
        {
            this.ReqAdapterName = "_" + Md5Util.Build(typeInfo.FullName) + "_REQ_ADAPTER";
            sb.AppendLine("[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]");
            sb.AppendLine($"sealed class {ReqAdapterName}:{A_MESSENGER_REQ_ADAPTER},{typeInfo.FullName}");
            sb.AppendLine("{");
            sb.AppendLine($"public {ReqAdapterName}(IActorRef target, IActorRef source, TimeSpan timeout, IMessengerOperatorSetter setter)");
            sb.AppendLine(":base(target, source, timeout, setter)");
            sb.AppendLine("{");
            sb.AppendLine("}");
            sb.AppendLine("private int useState = 0;");
            var methods = typeInfo.Methods;
            foreach (var method in methods)
            {
                var argsTypes = method.GetArgsTypes();
                var returnType = method.ReturnVoid ? "void" : method.GetReturnType().FullName;
                var asyncStr = method.ReturnVoid ? string.Empty : "async";
                sb.Append($"public {asyncStr} {returnType} {method.Name}");
                sb.Append("(");

                for (int i = 0; i < argsTypes.Count; i++)
                {
                    var item = argsTypes[i];
                    sb.Append($"{item.FullName} item{i}");
                    if (i != argsTypes.Count - 1)
                    {
                        sb.Append(",");
                    }
                }

                sb.Append(")");
                sb.AppendLine();
                sb.AppendLine("{");


                sb.AppendLine("if(System.Threading.Interlocked.CompareExchange(ref useState, 1, 0) == 1)");
                sb.AppendLine("{");
                sb.AppendLine($"throw new Exception({Generator.DQM}The adapter for interface { typeInfo.FullName } is in use{Generator.DQM});");
                sb.AppendLine("}");

                sb.AppendLine("this.setter.SetActorOpencode(ActorOpencode.NONE);");
                sb.AppendLine("this.setter.SetMessengerOpencode(MessengerOpencode.NONE);");

                var reqPackName = method.GetReqPackName();

                string packName;
                if (!string.IsNullOrEmpty(reqPackName))
                {
                    sb.AppendLine($"var req = new {reqPackName}();");
                    for (int i = 0; i < argsTypes.Count; i++)
                    {
                        sb.AppendLine($"req.item{i}=item{i};");
                    }

                    sb.AppendLine(MessengerPackageUtil.BuildPackage(method.GetProtocolId(), 1, "req", "0", out packName));
                }
                else
                {
                    sb.AppendLine(MessengerPackageUtil.BuildPackage(method.GetProtocolId(), 1, "null", "0", out packName));
                }

                if (method.ReturnVoid)
                {
                    sb.AppendLine($"this.target.Send({packName}, source);");


                    sb.AppendLine("System.Threading.Interlocked.Exchange(ref useState, 0);");
                }
                else
                {
                    sb.AppendLine($"var result = await this.target.CallAsync<MessengerPackage>({packName}, this.timeout, this.source);");
                    sb.AppendLine("this.setter.SetActorOpencode(result.ActorOpencode);");
                    sb.AppendLine("this.setter.SetMessengerOpencode((MessengerOpencode)result.Response.opencode);");

                    sb.AppendLine("System.Threading.Interlocked.Exchange(ref useState, 0);");

                    var genericArgs = method.GetReturnType().GetGenericArguments();
                    if (genericArgs.Count > 0)
                    {
                        sb.AppendLine($"return ({genericArgs[0].FullName})result.Response.data;");
                    }
                }

                sb.AppendLine("}");
            }

            sb.AppendLine("}");
        }

        public string Build()
        {
            return sb.ToString();
        }
    }
}
